from collections.abc import Callable
from typing import Protocol, runtime_checkable

import lldb


@runtime_checkable
class SynthMemberProtocol(Protocol):
    name: str
    def __call__(self, value: lldb.SBValue) -> lldb.SBValue: ...
    

class SynthMember:
    def __init__(self, name: str, func: Callable[[lldb.SBValue, str | None], lldb.SBValue]):
        self.name = name
        self.func = func

    def __call__(self, value: lldb.SBValue) -> lldb.SBValue:
        return self.func(value, self.name)


class SynthValue:
    def __init__(self, valobj: lldb.SBValue, members: list[SynthMemberProtocol]):
        self._value = valobj
        self.items: list[SynthMemberProtocol] = [*members]

    @property
    def value(self):
        return self._value

    @property
    def type(self) -> lldb.SBType:
        return self.value.GetType()

    def has_children(self):
        return self.num_children() > 0

    def num_children(self):
        return self.value.GetNumChildren() + len(self.items)

    def get_child_index(self, name: str):
        count: int = self.value.GetNumChildren()
        for n, i in enumerate(self.items):
            if i.name == name:
                return n + count
        return self.value.GetIndexOfChildWithName(name)

    def get_child_at_index(self, index: int):
        count: int = self.value.GetNumChildren()
        if index < count:
            return self.value.GetChildAtIndex(index)
        try:
            return self.items[index - count](self.value)
        except BaseException as e:
            return repr(e)
